﻿<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Test page for Json.Fliox.Hub to browse databases and send requests via HTTP and WebSocket to Hub">
    <meta name="color-scheme" content="dark light">
    <link rel="icon"     href="./explorer/img/Json-Fliox-53x43.svg" type="image/x-icon">
    <!-- <link rel="manifest" href="./manifest.json"> -->
    <meta name="theme-color" content="#ffffff">
    <title>JSON Fliox Hub</title>
    <script>
        // handle dark mode before everything else to avoid flickering
        window.setTheme = (mode) => {
            // console.log("setTheme", mode);
            document.documentElement.setAttribute('data-theme', mode);
            window.appConfig.monacoTheme    = mode == 'dark' ? "vs-dark" : "light";
            const themeColor                = mode == 'dark' ? "#1e1e1e" :"#ffffff"; // var(--background2)
            document.querySelector('meta[name="theme-color"]').setAttribute("content", themeColor);
            window.localStorage.setItem('theme', mode);
        }
        window.appConfig = {};
        var theme = window.localStorage.getItem('theme');
        if (!theme) {
            if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
                theme = 'dark';
            }
        }
        window.setTheme(theme);
    </script>
    <script src='./explorer/js/bundle.js'></script> <!-- todo: load deferred -->
    <script type="module">
        import { app  } from  "./explorer/js/index.js";
        window.app = app;
        app.initApp();
    </script>
    <style>
        :root {
            --header-height: 60px;
            --tab-height:    40px;
            --bar-width:      6px;
            --vbar-width:    16px;
            --mono-font: 'Consolas', 'Courier New', Courier, monospace;
            --mono-size:  14px;
            --sans-serif: 'Arial', 'Helvetica', 'sans-serif';
            /* light */
            --shadow:        0px 0px 4px 0px rgba(0,0,0,0.2);
            --selected-color:       #e1eeff;
            --selected-hover:       #cde3ff;
            --tab-color:            #e8e8e8;
            --tab-hover:            #e0e0e0;
            --tab-border:           #ffffff;
            --editor-border:        #e8e8e8;
            --background:           #f4f4f4;
            --background2:          #ffffff;    --bg-transparent:  #ffffffe8;
            --background3:          #f0f0f0;
            --item-hover:           #f4f4f4;
            --cell-hover:           #e8e8e8;
            --command-bar:          #d9d9ff;
            --link-color:           #d400b8;
            --link-deco:            #00000040;
            --color:                #000;
            --doc-link:             #5555c0;
            --table-bar:            #e8e8e8;
            --oas-color:            #55cf42;
            --graphql-color:        #ff6dcf;
            --diagram-color:        #6c6cff;
            --sub-color:            #909090;
            --sub-hover:            #b5ffae;
            --update-start:         #66ff66;
            --update-end:           #66ff6600;
            --sub-border:           #d0d0d0;
            --sub-delete:           #d00000;
        }
        [data-theme="dark"] {
            --shadow:        0px 0px 3px 0px rgba(255,255,255,0.2);
            --selected-color:       #3b38b7;
            --selected-hover:       #4e4cc7;
            --tab-color:            #303030;
            --tab-hover:            #606060;
            --tab-border:           #1e1e1e;
            --editor-border:        #000;
            --background:           #000;
            --background2:          #1e1e1e;    --bg-transparent:  #000000c0;
            --background3:          #000;
            --item-hover:           #303030;
            --cell-hover:           #404040;
            --command-bar:          #5f5f92;
            --link-color:           #e64bd1;
            --link-deco:            #ffffff60;
            --color:                #fff;
            --doc-link:             #a0a0ff;
            --table-bar:            #444;
            --oas-color:            #009900;
            --graphql-color:        #de33a6;
            --diagram-color:        #0000ee;
            --sub-color:            #a0a0a0;
            --sub-hover:            #277f1e;
            --update-start:         #0e8e00;
            --update-end:           #0e8e0000;
            --sub-border:           #606060;
            --sub-delete:           #ffa0a0;
        }

        /* text-decoration: underline;      text-decoration-color: var(--link-deco) !important;
           text-decoration-thickness: 2px;  color: color: var(--link-color) !important */
        .refLinkDecoration          { color: var(--link-color) !important;  }
        .refLinkDecoration:hover    { text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--link-deco) !important; }
        .docLink:link,
        .docLink:visited            { color: var(--doc-link); }

        .navigate           { cursor: pointer; font-size: 14px; height: 22px; width: 22px; text-align: center;
                              user-select: none; -moz-user-select: none;
                              background-color: var(--tab-color); border-radius: 2px;}
        .navigate:hover     { background-color: var(--tab-hover); }
        .navigate:active    { background-color: var(--background3); font-size: 13.5px; box-shadow: inset 1px 1px 1px #00000010; }
        .verticalAlign      { display: flex; justify-content: center; align-items: center; }

        .selectAll          { margin-right:8px; line-height: 5px; font-size: 7px; white-space:nowrap; text-align: left; }
        .selectAll span     { opacity: 0.3; }
        .selectAll:active   { font-size: 7.5px; }

        body::after {
            content: "";
            background-image: url('./explorer/img/paint-splatter.svg'); /* from https://freesvg.org/ */
            background-repeat: repeat; background-position: 5% 5%;
            opacity: 0.1;
            top: 0; left: 0; bottom: 0; right: 0;
            position: absolute;
            z-index: -1;   
        }

        a,         a > *            { color: var(--link) }
        a:visited, a:visited > *    { color: var(--visited) }
        body                        { color: var(--color); }

        /** ---------------------------------------------- Grid Layout ----------------------- */
        body {
            display: grid;
            font-size: 16px;    /* ChromeOS */
            grid-template-areas: 
                "body-header"
                "body-tabs"
                "body-playground"
                "body-explorer"
                "body-events"
                "body-settings";
            grid-template-columns:  1fr;
            grid-template-rows:     0 var(--tab-height) 0 1fr 0; /* dynamically overwritten by body.style */
            grid-gap:   0px;
            margin:     0;
            height:     100vh;
        }
        .description                { background: var(--bg-transparent); box-shadow: 0px 0px 80px 80px var(--bg-transparent); }
        .miniHeader .description    { background: unset; box-shadow: unset; }


        .editor { height: 100%; }
        
        #playground {
            display: none;
            grid-template-areas: 
                "pl-header          pl-header   pl-header"
                "pl-title-req       .           pl-title-resp"
                "pl-request         pl-bar      pl-response";
            grid-template-columns:  300px var(--bar-width) 1fr;  /* dynamically overwritten by #playground.style */
            grid-template-rows:     28px  25px 1fr;
            grid-gap:   0px;
        }

        #explorer {
            display: grid;
            grid-template-areas: 
                "ex-filter      .           ex-filter-input     ex-filter-input     ex-filter-input"
                "ex-empty       .           ex-bar0             ex-bar0             ex-bar0"
                "ex-empty       .           ex-schema           .                   ex-action"
                "ex-link-db     .           ex-link-container   .                   ex-link-entity"
                "ex-result      ex-result   ex-result           ex-result           ex-result"
                "ex-database    ex-bar1     ex-container        ex-bar2             ex-entity";
            grid-template-columns:  150px var(--bar-width) 400px var(--bar-width) 1fr;    /* dynamically overwritten by #explorer.style */
            grid-template-rows:     40px 3px 28px 19px 14px 1fr;
            grid-gap:   0px;
        }

        #events {
            display: none;
            grid-template-areas: 
                "ev-head        ev-head     ev-head"
                "ev-title-tree  .           ev-title-events"
                "ev-tree        ev-bar1     ev-editor";
            grid-template-columns:  200px var(--bar-width) 1fr;    /* dynamically overwritten by #explorer.style */
            grid-template-rows:     0px  26px 1fr;
            grid-gap:   0px;
        }

        #explorerEdit {
            display: grid;
            grid-template-areas: 
                "edit-command"
                "edit-bar"
                "edit-response";
            grid-template-columns:  1fr;
            grid-template-rows:     0 0 1fr; /* dynamically overwritten by #explorerEdit.style */
            grid-gap:   0px;
        }
        /** ---------------------------------------------- Grid Layout end ----------------------- */

        .dropbtn {
            color:      var(--color);
            background-color: var(--background);
            padding:    0px;
            border:     0px solid #aaa;
            width:      22px;
            height:     22px;
        }
        .dropbtn::before { opacity: 0.4; content: "▼"; }
        .authInput { width: 70px; }
        .authInputEye {
            margin-left: -16px;
            padding-right: 2px;
            cursor: pointer;
            color:  fieldtext;
        }
        .authInputEye::after { content: "⚇";  }
        .dropdown {
            position: relative;
            display: inline-block;
        }
        .dropdown-content {
            display: none;
            position: absolute;
            background-color: #f1f1f1;
            min-width: 110px;
            box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
            z-index: 1;
            cursor: default;
            font-size: 14px;
            right: 0;
            margin-right: -4px;
            cursor: pointer;
            border-radius: 4px 4px;
            overflow: hidden
        }
        .dropdown-content div {
            color: black;
            padding: 2px 6px;
            text-decoration: none;
            display: block;
        }
        .dropdown-content div:hover         { background-color: #ddd; }
        .dropdown:hover .dropdown-content   { display: block;}
        .dropdown:hover .dropbtn            { background-color: var(--tab-hover); }

        .auth-pending::after                { content: "◠"; color: var(--color); animation: spin 1s linear infinite; display: inline-block;  }
        .auth-success::after                { content: "✔"; color: #00a300; }
        .auth-error::after                  { content: "?"; }
        .auth-failed::after                 { content: "!"; color: #ff3c3c; }

        .user-list                          { width: 18px; display: inline-flex; }
        .user-list.remove::after            { content: "×"; opacity: 0.4; }
        .user-list:hover::after             { color: red; opacity: 1; text-shadow: 2px 2px 6px #000000;  }
        

        html            { max-width: 100%; overflow-x: hidden; } /* disable horizontal scrollbar */
        a               { text-decoration:  none; }
        tr              { vertical-align:   top; }
        button          { width:            80px; font-size: 14px;}
        .inline         { display:          inline; }
        h2              { margin:           0px;  }
        label           { display:          inline;  }
        input           { display:          inline; font-size: 13px; /* ChromeOS */ }
        .nobreak *      { white-space:      nowrap; }
        .pre *          { white-space:      pre; }
        table.header td { padding-right:    0px; }
        li              { font-size:        14px;}
        sup             { font-size: 10px; color: rgb(143, 143, 255);}
        ul              { margin: 5px; padding-left: 15px; }
        td              { padding: 0px; }
        /* [html - How do I make a placeholder for a 'select' box? - Stack Overflow] https://stackoverflow.com/questions/5805059/how-do-i-make-a-placeholder-for-a-select-box */
        select:required:invalid     {  color: gray;  }
        option[value=""][disabled]  {  display: none;  }
        option                      {  color: black; }

        .vbar           { background-color: var(--background); touch-action: none; /** prevent triggering browser default event like reload */ }

        .toggleDescription                      { width: 23px; height: 23px; transform: rotate(270deg) translate(-2px, 5px);
                                                  opacity: 1; cursor: pointer; border-radius: 2px; }
        .toggleDescription::before              { opacity: 0.5; content: "❯" }
        .toggleDescription:hover                { background-color: var(--tab-hover); }
        .miniHeader .toggleDescription          { width: 53px; height: 43px; transform: rotate(0) scale(0.8) translate(-0px, -17px);
                                                  background-image: url("./explorer/img/Json-Fliox-53x43.svg");  opacity: 1;  }
        .miniHeader .toggleDescription::before  { content: unset }
        #toggleDescription                      { min-width: 33px;}
        .miniHeader #toggleDescription          { min-width: 60px;}

        .toggleTheme        { width: 22px; height: 22px; background: var(--background); border: 1px solid var(--tab-border); border-radius: 2px;
                              display: flex; justify-content: center; align-items: center;  cursor: pointer; }
        .toggleTheme:hover  { background-color: var(--tab-hover); }
        
        toggleTheme         { padding: 0px; font-size: 12px; }

        toggleTheme::before { content: "☀"; text-align: center; }
        [data-theme="dark"]
        toggleTheme::before { content: "🌖︎"; }
        

        .tab            { cursor: pointer; background-color: var(--tab-color); color: var(--color)!important;;
                          border: 1px solid var(--tab-border); margin: 0px; padding: 5px 10px; }
        .tab:hover      { background-color: var(--tab-hover);   border-bottom: 1px solid #ffffff00; }
        .tab.selected   { background-color: var(--background);  border-bottom: 1px solid #ffffff00; }
        .tabContent     { display: none; border-top: none; background-color: var(--background); }

        .project        { margin: 0; overflow: hidden; display: flex; box-shadow: var(--shadow); background-color: rgba(0,0,0,0.1);  }

        .project-name   { height: 18px; padding: 0px 4px; margin: 0; background-color: var(--background2);   color: var(--color);
                          border-top-left-radius:  3px;  border-bottom-left-radius: 3px; border: 1px solid  var(--background2); border-right: 0; }
        .project-env    { height: 18px; padding: 0px 4px; margin: 0; background-color: rgb(117, 117, 117); color: white;
                          border-top-right-radius: 3px; border-bottom-right-radius: 3px; border: 1px solid var(--background2); border-left: 0; }

        .applyFilter        { cursor: pointer; border-radius: 2px; width: 24px; height: 14px; display:inline-block; text-align: center;
                              margin-right: 1px; font-size: 11px; padding-top: 3px; padding-bottom: 3px; }
        .applyFilter:hover  { background-color: var(--tab-hover); }
        .applyFilter:active { background-color: var(--background3); box-shadow: inset 1px 1px 1px #00000010; }

                      #entityFilter         { opacity: 0.3; }
                      #removeFilter         { visibility: hidden; }
                      #entityFilter:focus,
        .filterActive #entityFilter         { opacity: 1; }
        .filterActive #removeFilter         { visibility: visible; }
        

        .input          { height: 18px; font-size: var(--mono-size); color: var(--color); font-family: var(--mono-font); 
                          background-color: transparent;  margin: 0px 0px 5px 0px; padding: 0px 3px;    border: 0.5px solid #88888800;       }
        .input:hover    { background-color: transparent;  margin: 0px 0px 5px 0px; padding: 0px 3px;    border: 0.5px solid var(--tab-border); }
        .input:focus    { background-color: var(--background2);  outline: none !important;              border: 0.5px solid #88888800;       }
        .input::placeholder { opacity: 0.3; color: var(--color); font-family: var(--sans-serif); font-size: 12px; }


        .caret                      { width: 12px; height: 12px; text-align: center; font-size: 10px;}
        .caret::before              { content: "❯"; } /* using ::before avoids getting copied to clipboard */
        .caret:hover                { font-weight: bold; color: darkturquoise;}

        .catalogs > ul              { padding-left: 0px; padding-right: 0px; }


        .treeParent > ul    { margin-top: 0px; margin-left: 0px; margin-right: 0px; display: none; }
        .treeParent.active > ul    { display: block; }
        .treeParent.active > div > div:nth-child(1)  { -ms-transform: translateY(3px) rotate(90deg); -webkit-transform: translateY(3px) rotate(90deg); transform: translateY(3px) rotate(90deg) }
        .catalogs li                { list-style:none; }
        .catalogs li                { cursor: pointer; }

        .updated                    { animation-name: update;  animation-duration: 3s; animation-timing-function: ease-in; }
        .updated2                   { animation-name: update2; animation-duration: 3s; animation-timing-function: ease-in; }
        @keyframes update           { from { background-color: var(--update-start); } to  {background-color: var(--update-end); } }
        @keyframes update2          { from { background-color: var(--update-start); } to  {background-color: var(--update-end); } }

        .subscribed .creates        { color: var(--color); }
        .subscribed .deletes,
        .subscribed .error          { color: var(--sub-delete); }
        .subscribed .patches        { color: var(--color); }
        .sub                        { border: 1px solid #00000000; border-radius: 3px; font-size: 12px; height: 13px; min-width: 25px;
                                      color: var(--sub-color); align-self: center; text-align: right; margin-left: 4px; padding-left: 2px; padding-right: 2px; }
        .sub:hover                  { background-color: var(--sub-hover); }

        .dbMessages:hover       .sub:empty,
        .dbMessage:hover        .sub:empty,
        .clusterContainer:hover .sub:empty { text-align: center; }

        .dbMessages:hover       .sub:empty::before,
        .dbMessage:hover        .sub:empty::before,
        .clusterContainer:hover .sub:empty::before { content: "sub"; }
        
        .subscribed,
        .subscribed:hover           { border: 1px solid var(--sub-border); }

        .dbMessage.selected,
        .dbMessage.selected:hover,        
        .dbMessages.selected,
        .dbMessages.selected:hover,
        .clusterDatabase.selected,
        .clusterDatabase.selected:hover,
        .clusterContainer.selected,
        .clusterContainer.selected:hover  { background-color: var(--selected-color); }

        .dbMessage:hover,
        .dbMessages:hover,
        .clusterDatabase:hover,
        .clusterContainer:hover     { background-color: var(--item-hover); }

        .dbMessage,
        .dbMessages,
        .clusterDatabase,
        .clusterContainer           { padding-left: 0px; cursor: pointer; list-style:none; display: flex; }
        .dbMessage        div:nth-child(1),
        .dbMessages       span:nth-child(2),
        .clusterDatabase  span:nth-child(2),
        .clusterContainer div:nth-child(1)  { flex-grow: 1; }

         .oas                       { background-color: var(--oas-color); padding: 0px 2px; font-size: 12px; border-radius: 3px; font-weight: bold; }
        a.oas:link,
        a.oas:visited               { color: white; }

         .graphql                   { background-color: var(--graphql-color); padding: 0px 2px; font-size: 12px; border-radius: 3px; font-weight: bold; }
        a.graphql:link,
        a.graphql:visited           { color: white; }

         .diagram                   { background-color: var(--diagram-color); padding: 0px 2px; font-size: 12px; border-radius: 3px; font-weight: bold; }
        a.diagram:link,
        a.diagram:visited           { color: white; }

         .schemaExports             { color: var(--doc-link); }
        a.schemaExports:link,
        a.schemaExports:visited     { color: var(--doc-link); }

        .entities                       { width: 100%; padding-left: 0px; font-size: 14px; border-collapse: collapse; margin: 0 0 4px 0px;
                                          table-layout: fixed;
                                          user-select: none; -moz-user-select: none; /* Firefox: otherwise artifact for outline used for focused cell */ }
        .entities tr th                 { text-align: left; font-weight: bold; font-size: 12px;  position: sticky; top: 0; color: #999;
                                          margin: 0; padding: 0; }
        .entities tr th:first-child     { left: 0; z-index: 1;  } /* sticky top/left cell */
        .entities tr th:last-child      { padding: 0; width: 4px; }
        .entities tr th div             { height: 14px; border-bottom: 2px solid var(--table-bar); overflow: hidden; text-overflow: ellipsis;
                                          margin-bottom: 2px; padding-top: 4px; padding-left: 2px; cursor: default; }
        .entities tr th div:first-child { background-color: var(--background2); }

        .entities tr th:first-child div { border-bottom: 2px solid var(--background2); }
        .thGrip                         { top: 0; right: 0; bottom: 0; width: 6px; position: absolute; pointer-events: all;
                                          user-select: none; -moz-user-select: none; /* disable text selection while dragging */ }
        .entities tr th div.thGrip:hover{ background: var(--background); border-right: 3px solid #3c94ed;}

        .entities tr td:first-child { position: sticky; left: 0; padding-left: 0px;  }
        .entities tr td             { padding-left: 2px; overflow: hidden; height: 20px; line-height: 20px; text-overflow: ellipsis; } /* fixed height for all rows */
        .entities tr td input       { height: 12px; margin: 0px 2px 0 2px; vertical-align: middle;  }
        .entities tr td *           { pointer-events: none; }
        .entities tr td:nth-child(1){ width: 14px; background-color: var(--background2); padding-left: 0; padding-right: 0px; }
        .entities tr td:last-child  { padding-right: 2px; }

        .entities tr td:nth-child(1) input                      { visibility: hidden; }
        .entities tr.selected td:nth-child(1) input             { visibility: visible; }
        .entities tr                td:nth-child(1):hover input { visibility: visible;  opacity: 0.4;  }
        .entities tr.selected:hover td:nth-child(1)       input { opacity: 1; background-color: red; }

        .entities tr *                          { cursor: pointer; }
        .entities tr.selected *                             { background-color: var(--selected-color); }
        .entities tr:hover td:not(:first-child)             { background-color: var(--item-hover); }
        .entities tr.selected:hover td:not(:first-child)    { background-color: var(--selected-color); }

        .cellCount                                          { color: grey; }
        .entities tr          td:not(:first-child):hover            { background-color: var(--cell-hover)     !important; }
        .entities tr.selected td:not(:first-child):hover            { background-color: var(--selected-hover) !important; }
        #entityExplorer:focus       .entities tr td.focus           { outline: 2px solid rgb(145, 145, 255); outline-offset: -2px; }
    /*  #entityExplorer:not(:focus) .entities tr td.focus           { background-color: var(--cell-hover);      }
        #entityExplorer:not(:focus) .entities tr.selected td.focus  { background-color: var(--selected-hover);  } */

        .entities tr td                     { scroll-margin-top: 20px; scroll-margin-left: 16px; }

        .entities tr td.editCell div textarea { pointer-events: all; font-family: var(--sans-serif);
                                                font-size: 14px; color: var(--color); background-color: var(--background2); border-radius: 2px;
                                                outline: none; box-sizing: border-box; padding: 0; margin: 0; box-shadow: 0px 1px 3px 2px rgba(0,0,0,0.1); }

        /* [The Cleanest Trick for Auto growing Textarea | CSS-Tricks - CSS-Tricks] https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */
        .entities tr td.editCell              { position: relative; width: max-content; overflow: visible; padding: 0; }
        .entities tr td.editCell div          { position: absolute; width: inherit; display: grid; grid-area: 1 / 1 / 2 / 2; z-index: 1; }
        .entities tr td.editCell div textarea { resize: none; overflow:hidden; }
        .entities tr td.editCell div::after   { content: attr(data-replicated-value) " "; white-space: pre-wrap; visibility: hidden; }
        .entities tr td.editCell div textarea,
        .entities tr td.editCell div::after   { grid-area: 1 / 1 / 2 / 2; font: inherit; line-height: 18px; border: 2px solid var(--color); }



        .database                           { padding-left: 0px; margin-right: 0px; }
        .database li > ul                   { padding-left: 0px; margin-top: 0px; margin-left: 0px; }
        .database li ul li                  { padding-left: 0px; cursor: pointer; list-style:none; display: flex; justify-content: space-between; }
        .database li ul li.selected         { background-color: var(--selected-color); }
        .database li ul li:hover            { background-color: var(--item-hover); }
        .database li ul li:hover .command   { display: block;  }

        #databaseInfo                       { margin-right: 5px; }
        #databaseInfo.selected              { background-color: var(--selected-color); }
        #databaseInfo:hover                 { background-color: var(--item-hover); }

        /*--- filter editor ---*/
    /*  #filterArea              > div .monaco-editor-background    { background-color: var(--background);  }
        #explorer.filterActive   > div .monaco-editor-background,
        #filterArea:focus-within > div .monaco-editor-background    { background-color: var(--background2); } */
                      #filterArea   { border: 1px solid var(--background); }
        .filterActive #filterArea   { border: 1px solid #7f7fff9e; }

        .command                    { display: none; padding-left: 2px; padding-right: 2px; font-size: 12px; }
        .command:hover              { background-color: var(--oas-color); color: white;  }
        .command:active             { background-color:#22ee22; }
        .command::before            { content: "▶"; width: 26px; display: inline-block; text-align: center; }

        .settings li                { list-style: none; }

        .reload                     { cursor:pointer; opacity:0.5; font-size: 13px; border-radius: 2px;
                                      user-select: none; -moz-user-select: none; }
        .reload::after              { content: "↻"; padding-left: 7px; padding-right: 7px; }    /* using ::after avoids getting copied to clipboard */
        .reload:hover               { background-color: var(--tab-hover); opacity:1; }
        .reload:active::after       { background-color: var(--background3); font-size: 12px; box-shadow: inset 1px 1px 1px #00000010; }

        .spinner {
            border:         3px solid #f3f3f3;
            border-top:     3px solid #3498db;
            border-radius:  50%;
            width:          8px;
            height:         8px;
            animation:      spin 1s linear infinite;
            display:        inline-block;
            margin-left:    10px;
        }
        @keyframes spin {
            0%      { transform: rotate(0deg); }
            100%    { transform: rotate(360deg); }
        }
    </style>
</head>

<body style="grid-template-rows: 0 var(--tab-height) 0 1fr 0;
             font-family: var(--sans-serif); background-color: var(--background2);"
             class="miniHeader">
  <!--- [monaco-editor-samples/index.html at main · microsoft/monaco-editor-samples]
        https://github.com/microsoft/monaco-editor-samples/blob/main/browser-script-editor/index.html --->
  <link rel="stylesheet" data-name="vs/editor/editor.main" href="./monaco-editor/min/vs/editor/editor.main.css"/>
  <script>
     var require = { paths: { vs: './monaco-editor/min/vs' } };
  </script>
  <script src="./monaco-editor/min/vs/loader.js"></script>
  <script src="./monaco-editor/min/vs/editor/editor.main.nls.js"></script>
  <script src="./monaco-editor/min/vs/editor/editor.main.js"></script>
  <script type="module">
    app.setupEditors();
  </script>

  <div class="description" style="grid-area: body-header; overflow: hidden;">
  <table class="header" style="width: max-content;">
    <tr>
      <td style="padding: 5px;">
        <a href="https://github.com/friflo/Friflo.Json.Fliox" target='_blank' rel='noopener'>
          <img src='./explorer/img/Json-Fliox-53x43.svg' width="53" height="43" alt="friflo JSON Fliox">
        </a>
      </td>
      <td style="padding-left: 5px; padding-right: 5px;">
        <h1 style="margin-top: 10px; font-size: 18px;">JSON<br>Fliox Hub</h1>
      <td>
        <ul>
            <li>DB: CRUD, query, cursors, relations & patch</li>
            <li>API: REST, OpenAPI, GraphQL, Batch & WebSocket</li>
            <li>C# ORM: remote, file-system, in-memory, NoSQL</li>
        </ul>
      </td>
      <td>
        <ul>
            <li>Sub-millisecond Messaging & Pub-Sub<sup>1</sup></li>
            <li>Token based user authentication<sup>1</sup></li>
            <li>Role based task authorization<sup>1</sup></li>
        </ul>
      </td>
      <td>
        <ul>
            <li>Host multiple databases by Hub<sup>1</sup></li>
            <li>Schema validation<sup>1</sup> & generation<sup>1</sup></li>
            <li>Monitor requests, tasks & events<sup>1</sup></li>
        </ul>
      </td>
      <td>
        <span style="float:right;"><sup>1</sup> <span style="font-size: 11px; font-style: italic;">optional</span></span>
      </td>
    </tr>
  </table>
  </div>
  <!-- [How To Create Tabs] https://www.w3schools.com/howto/howto_js_tabs.asp -->
  <div style="grid-area: body-tabs; overflow:initial; white-space:nowrap; margin-top: 10px; display:flex;">
    <div id="toggleDescription" onclick="app.toggleDescription()" style="border-bottom: 1px solid var(--tab-border); margin: 0;" title="toggle Hub description">
        <div class="toggleDescription" ></div>
    </div>
    <a class="tab" href="#" onclick="app.openTab('explorer')"   value="explorer" style="border-top-left-radius:  3px;"
       title="explore databases and containers, edit entities and send commands">Explorer</a>
    <a class="tab" href="#" onclick="app.openTab('events')" value="events"
       title="view subscription events received from WebSocket">Pub-Sub</a>
    <a class="tab" href="#" onclick="app.openTab('playground')" value="playground"
       title="edit and send batch requests containing an array of tasks via HTTP POST or WebSocket">Playground</a>
    <a class="tab" href="#" onclick="app.openTab('settings')"   value="settings" style="border-top-right-radius: 3px;"
       title="settings and documentation"><b>⋮</b></a>
    <span style="border-bottom: 1px solid var(--tab-border); display: grid; flex-grow: 1; min-width: 10px; justify-content: center; align-items: baseline;">
      <a id="projectUrl" style="margin: 0 5px; display: grid;" target="_blank" href="https://github.com/friflo/Friflo.Json.Fliox" title="xxxx">
        <div class="project">
            <span id="projectName" class="project-name">Fliox Hub</span><!--
         --><span id="envEl"       class="project-env"  ></span>
        </div>
      </a>
    </span>
    <span style="border-bottom: 1px solid var(--tab-border); font-size: 13px;">
      <div style="background: var(--background); box-shadow: var(--shadow); padding: 1px 1px 1px 10px; border-radius: 2px; border: 1px solid var(--tab-border)">
        <label for="user">user</label>   <input onchange="app.setUser(value); app.checkAuth();"  spellcheck="false" type="text" id="user"  class="authInput" value="admin">&nbsp;
        <label for="token">token</label>
        <span>
            <input onchange="app.setToken(value); app.checkAuth();" spellcheck="false" type="password" id="token" class="authInput" value="admin" style="padding-right: 16px;">
            <span class="authInputEye" id="togglePassword" onclick="app.togglePassword()" style="opacity: 0.4;"></span>
        </span>
        <div class="dropdown" >
          <button class="dropbtn" aria-label="selectUser" title="recent logins"></button>
          <div id="userList" class="dropdown-content">
            <div onclick="app.selectUser(this)">admin</div>
          </div>
        </div>
        <span class="dropdown">
          <div id="authState" style="font-size: 14px; width: 16px; text-align: center;" class="auth-pending"></div>
        </span>
      </div>
    </span>
    <span title="toggle theme dark / light" style="border-bottom: 1px solid var(--tab-border); padding-left: 5px; padding-right: 5px;">
        <div onclick='app.toggleTheme()' class="toggleTheme" style="box-shadow: var(--shadow);">
            <toggleTheme ></toggleTheme>
        </div>
    </span>
    
  </div>

  <!-- ------------------------------------------- Playground ------------------------------------------- -->
  <div style="grid-area: body-playground; overflow: hidden; padding-top: 10px;" id="playground" class="tabContent">
    <div style="grid-area: pl-header; overflow:hidden; white-space:nowrap; padding-left: 7px; padding-top: 1px;">
      <select required title="example-requests" id="example" onchange="app.playground.onExampleChange()"></select>
      <button type="button" onclick="app.playground.postSyncRequest()" title="POST request via HTTP (Ctrl+Alt+P)">POST</button>

      &nbsp;&nbsp;&nbsp;&nbsp; <small>WebSocket</small> &nbsp;
      <button type="button" onclick="app.playground.sendSyncRequest()"  title="Send websocket request (Ctrl+Alt+Enter)">send</button>
      &nbsp;&nbsp;
      <button type="button" onclick="app.playground.connectWebsocket()" title="connect WebSocket">connect</button> 
      <button type="button" onclick="app.playground.closeWebsocket()"   title="close WebSocket">close</button> &nbsp;
      <small id="socketStatus" class="inline">disconnected</small> &nbsp;&nbsp;&nbsp;&nbsp;
    </div>

    <div style="grid-area: pl-title-req; overflow: hidden; padding-left: 7px;">
        <small>
            <a href="./schema/protocol/html/schema.html#Friflo.Json.Fliox.Hub.Protocol.SyncRequest" class="docLink"
               title="open request definition in new tab"  target="_blank" rel="noopener noreferrer"">SyncRequest</a>
        </small>
        <div style="opacity: 0.6; float: right;" title="properties added to request on: WebSocket send">
            <code>req:<div id="req" class="inline"><span style="opacity: 0.4;">-</span></div></code>
            <code>ack:<div id="ack" class="inline"><span style="opacity: 0.4;">-</span></div></code>
            <code>clt:<div id="clt" class="inline"><span style="opacity: 0.4;">-</span></div></code>
        </div>
    </div>
    
    <div style="grid-area: pl-title-resp; overflow: hidden; padding-right: 7px;" class="nobreak">
      <small>
        <a href="./schema/protocol/html/schema.html#Friflo.Json.Fliox.Hub.Protocol.SyncResponse" class="docLink"
           title="open response definition in new tab"  target="_blank" rel="noopener noreferrer">SyncResponse</a>
      </small> <small id="response-state"></small>
      
      <span style="float:right;">
        <small style="opacity: 0.7; cursor: default;" title="all WebSocket messages: request, response & event &#10;DevTools · Network · ws-* · Messages">ⓘ</small>&nbsp;
        <small>events: <span id="subscriptionCount">0</span></small>&nbsp;
        <code style="font-weight: bold">"seq": <span id="subscriptionSeq">-</span></code>
      </span>
    </div>

    <!-- used overflow: visible; to allow editor tooltip warning displayed outside editor rectangle -->
    <div  style="grid-area: pl-request; overflow: hidden;">
        <div class="editor" id="requestContainer"></div>
    </div>
    <div id="plBar" style="grid-area: pl-bar; cursor: ew-resize;" class="vbar" onpointerdown="app.startDrag(event, 'playground', id, true)"></div>
    <div class="editor" style="grid-area: pl-response; overflow: hidden;">
        <div class="editor" id="responseContainer"></div>
    </div>
  </div>

  <!-- ------------------------------------------- Explorer ------------------------------------------- -->
  <div style="grid-area: body-explorer; overflow: hidden; padding-top: 3px;"
       id="explorer" class="tabContent">
    <!-- ------- filter ------- -->
    <div style="grid-area: ex-filter; display: flex; flex-direction: row;">
      <div style="width: 5px; flex-shrink: 0;"></div>
      <div title="Toggle query filter" class="navigate" style="margin-top: 3px; flex-shrink: 0;" onclick="app.toggleFilterBar()">
        <div style="filter: grayscale(100%); opacity: 0.4; font-size: 9px; padding-top: 4px;">🔎</div>
      </div>
      <span style="flex-grow: 1;"></span>
      <span id="filterRow" style="visibility: hidden; overflow: hidden; padding: 2px;">
        <span style="display: flex; flex-direction: column;">
          <span style="display: flex; flex-direction: row; height: 18px;">
            <a id="removeFilter" style="width: 20px; color: red; font-weight: bold;"
               href="#" class="applyFilter" title="Remove query filter (Ctrl + 🡄)" onclick="app.removeFilter()">✖</a>
            <a href="#" class="applyFilter" title="Apply query filter (Ctrl + Enter)"  onclick="app.applyFilter()">🔎</a>
            <a id="filterLink" title="Open query in new tab" style="opacity: 0.7;" href="#" target="_blank" onclick="app.updateFilterLink()">
              <small>filter</small>
            </a>
          </span>
          <span style="display: flex; flex-direction: row;">
            <span style="flex-grow: 1;"></span>
            <a href="https://github.com/friflo/Friflo.Json.Fliox/blob/main/Json/Fliox.Hub/Client/README.md#query-filter"
               class="docLink" target='_blank'
               title='Open documentation for&#013;query filter ⊂ LINQ where ≈ SQL WHERE&#013;e.g.  o => o.id == 42'>
               <span style="font-size: 10px;">ⓘ LINQ</span>
            </a>
          </span>
        </span>
      </span>
    </div>
    <div id="filterArea" style="grid-area: ex-filter-input; overflow: hidden; display: flex; visibility: hidden;">
        <!-- <input id="entityFilter" style="width: 100%;" class="input" type="text" spellcheck="false"
            title       = 'lambda expression predicate ⊂ LINQ where ≈ SQL WHERE&#013;e.g.  o => o.id=="abc"'
            placeholder = 'filter expression'
            onkeydown="app.filterOnKeyDown(event)" onchange="app.updateFilterLink()"> -->
        <div class="editor" id="filterContainer" style="width: 100%;"></div>
    </div>

    <div id="exBar0" style="grid-area: ex-bar0; cursor: ns-resize;" class="vbar"
         onpointerdown="app.startDrag(event, 'explorer', id, false)"></div>

    <!-- ------- schema link, entity command/type ------- -->
    <div style="grid-area: ex-empty; overflow:hidden; z-index: 1; margin-left: 30px; cursor: ns-resize; background-color: var(--background);"
         onpointerdown="app.startDrag(event, 'explorer', 'exBar0', false)">
    </div>
    <div style="grid-area: ex-schema; display:flex; height: 23px; overflow:hidden;">
      <span  id="explorerTools"></span>
      <small id="catalogSchema" class="docLink" style="display: flex; justify-content: center; align-items: center;"></small>
      <span class="vbar" style="flex-grow: 1; cursor: ns-resize;" onpointerdown="app.startDrag(event, 'explorer', 'exBar0', false)"></span>
      <!-- <div title="events" class="navigate" onclick="app.editor.showEvents()">
        <span style="vertical-align: text-bottom; font-size: 10px;">💬</span>
      </div> -->
    </div>
    <div style="grid-area: ex-action; display:flex; overflow:hidden; white-space:nowrap; height: 23px;">
        <div title="Go back (Alt + 🠔)"    class="navigate" onclick="app.editor.navigateEntity(app.editor.entityHistoryPos - 1)">⏴</div>
        <div style="width:3px;"></div>
        <div title="Go forward (Alt + ➞)" class="navigate" onclick="app.editor.navigateEntity(app.editor.entityHistoryPos + 1)">⏵</div>
        <div style="width:8px;"></div>
        <span id="entityTools" style="display: contents;">
            <button id="entityDelete" disabled type="button" style="margin-right: 2px" onclick="app.editor.deleteEntitiesAction()"
              title="Delete entities using a DELETE request.&#13;To delete select entities in list or add ids in GET field below and press enter.">Delete</button>
            <button id="entitySave"   disabled type="button" style="margin-right: 2px" onclick="app.editor.saveEntitiesAction()"
              title="Save - aka upsert - entities using a PUT request (Ctrl+S)">Save</button>
            <button id="entityPatch"  disabled type="button" style="margin-right: 4px" onclick="app.editor.patchEntitiesAction()"
              title="Merge the given members to the stored entity using a PATCH request (Ctrl+P)&#13;see RFC 7386 - JSON Merge Patch">Patch</button>
            <!-- <div title="format JSON" class="navigate verticalAlign" onclick="app.editor.navigateEntity(app.editor.entityHistoryPos + 1)">
                <small style="opacity: 0.7;">{ }</small>
            </div> -->
            &nbsp;
            <small id="entityType" title="entity type" style="display: flex; justify-content: center; align-items: center;"><span style="opacity: 0.4;">entity type</span></small>
            <span style="flex-grow: 1; cursor: ns-resize;" onpointerdown="app.startDrag(event, 'explorer', 'exBar0', false)"></span>
        </span>
        <span id="commandTools" style="display: none;">
            <button type="button" onclick="app.editor.sendCommand()" title="Send command/message using POST (Ctrl+S)">Send</button>
            &nbsp;
            <!-- <div title="format JSON" class="navigate verticalAlign" onclick="app.editor.navigateEntity(app.editor.entityHistoryPos + 1)">
                <small style="opacity: 0.7;">{ }</small>
            </div> -->
            &nbsp;
            <small id="commandSignature" class="verticalAlign">command</small>
        </span>
        <span id="databaseTools" style="display: none;">
            <small id="databaseLabel" title="database schema" style="display: flex; justify-content: center; align-items: center;"></small>
            <span style="flex-grow: 1; cursor: ns-resize;" onpointerdown="app.startDrag(event, 'explorer', 'exBar0', false)"></span>
        </span>
        <span id="eventTools" style="display: none;">
            <small title="subscription events" style="display: flex; justify-content: center; align-items: center;">Subscription events</small>
        </span>
    </div>

    <!-- ------- resource titles/links ------- -->
    <div style="grid-area: ex-link-db; ; overflow:hidden; padding-left: 7px;">
        <small><a href="./rest" target="_blank" rel="noopener noreferrer" title="open databases hosted by Hub in new tab">databases</a></small>
    </div>
    <div style="grid-area: ex-link-container; overflow:hidden; white-space:nowrap; padding-left: 2px;">
        <small>
        <span id="readEntitiesDB"><span style="opacity: 0.4;">database/</span></span><!--
     --><span id="readEntities"><span style="opacity: 0.4;">container</span></span><!--
     --><span id="readEntitiesCount"  style="opacity: 0.7;"></span>
        </small>
    </div>
    <div style="grid-area: ex-link-entity; overflow:hidden; grid-template-columns: 200px 5px 1fr; white-space:nowrap; padding-left: 2px;">
        <span id="entityHeader" style="display: none;">
            <small style="display: flex; align-items: center;">
                <a     id="entityIdsContainer" href="#" style="opacity:0.7; margin-right:3px;"></a>
                <span  id="entityIdsCount"  style="opacity:0.7; min-width: 40px;"></span>
                <a     id="entityIdsGET"    style="text-align: end;" title="open entities in new tab" href="${getUrl}" target="_blank" rel="noopener noreferrer">GET</a>
                <span  id="entityIdsReload" class="reload" title='reload entity'></span>
                <input id="entityIdsInput"  class="input" style="flex-grow: 1; height: 16px; margin: 0; text-overflow: ellipsis; font-size: 12px; font-family: unset;"
                       title="add ids and press enter to read entities by id"
                       placeholder="read / delete entities by id" spellcheck="false"/>
                <div style="width:6px"></div>
            </small>
        </span>
        <span id="commandHeader" style="display: none;" >
        <small>
            <a id="commandAnchor" title="Open new tab and send the command/message using GET" href="${url}" target="_blank" rel="noopener noreferrer">${link}</a>
            <span id="commandDocs" style="margin-left: 20px; opacity: 0.6;">docs</span>
        </small>
        </span>
    </div>
    <div style="grid-area: ex-result; overflow:hidden;">
        <div id="writeResult" style="float:right; margin-right: 10px; font-size: 11px;"></div>
    </div>

    <!-- ------- database tree, container list, editors ------- -->
    <div style="grid-area: ex-database; background-color: var(--background2); overflow: auto; ">
      <div class="catalogs" id="clusterExplorer" style="display: block; min-height: 50px; white-space: nowrap;">
        <!-- [How To Create a Tree View] https://www.w3schools.com/howto/howto_js_treeview.asp -->
      </div>
    </div>

    <div id="exBar1" style="grid-area: ex-bar1; cursor: ew-resize;" class="vbar" onpointerdown="app.startDrag(event, 'explorer', id, true)"></div>

    <div style="grid-area: ex-container; background-color: var(--background2); overflow: auto; overscroll-behavior: auto contain;">
      <div id="entityExplorer"  style="display: block; white-space:nowrap; outline: 0px solid transparent;"
           onkeydown="app.explorer.explorerKeyDown(event)" tabindex="0"></div>
    </div>

    <div id="exBar2" style="grid-area: ex-bar2; cursor: ew-resize;" class="vbar" onpointerdown="app.startDrag(event, 'explorer', id, true)"></div>

    <!-- used overflow: visible; to allow editor tooltip warning displayed outside editor rectangle -->
    <div id="explorerEdit" style="grid-area: ex-entity; grid-template-rows: 0 0 1fr; overflow: visible;">

        <div id="commandValueContainer" style="grid-area: edit-command; display: unset;">
            <div class="editor" id="commandValue"></div>
        </div>
        <div id="commandParamBar" style="grid-area: edit-bar; cursor: ns-resize; background: var(--background); text-align: center;"
             class="vbar" onpointerdown="app.startDrag(event, 'explorerEdit', id, false)">
             <small style="vertical-align: top; opacity: 0.7;">command response</small>
        </div>
        <div style="grid-area: edit-response; display: flex;">
            <div id="entityContainer" style="flex-grow: 1; display: none;"></div>
            <div id="dbInfo" style="flex-grow: 1; display: none; padding-top: 15px; font-size: 13px; border-top: 2px solid var(--tab-border);">
                <div>
                    <table style="border-spacing: 10px 2px;">
                        <tr>
                            <td colspan="2" style="text-align: center; opacity: 0.6; border-bottom: 2px solid var(--table-bar);">database</td>
                        </tr>
                        <tr>
                            <td style="opacity: 0.6;">name</td> <td><span id="databaseName"></span></td>
                        </tr>
                        <tr style="opacity: 0.6;">
                            <td style="padding-bottom: 5px;">storage</td>   <td><span id="databaseStorage"></span></td>
                        </tr>

                        <tr>
                            <td colspan="2" style="text-align: center; opacity: 0.6; border-bottom: 2px solid var(--table-bar);">schema</td>
                        </tr>
                        <tr>
                            <td style="opacity: 0.6;">name</td> <td><span id="databaseSchema"></span></td>
                        </tr>
                        <tr>
                            <td style="opacity: 0.6; padding-bottom: 10px;">types</td> <td><span id="databaseTypes" style="opacity: unset;"></span></td>
                        </tr>
                        <tr>
                            <td colspan="2"><span id="schemaDescription" style="opacity: 0.7;"></span></td>
                        </tr>
                    </table>
                </div>
            </div>
        </div>

    </div>
  </div>

  <!-- ------------------------------------------- Events ------------------------------------------- -->
  <div id="events" class="tabContent" style="grid-area: body-events; overflow: hidden; padding-top: 5px;">
    <div style="grid-area: ev-head; overflow:hidden; white-space:nowrap; padding-left: 7px; padding-top: 1px; font-size: 13px;">
    </div>

    <div style="grid-area: ev-title-tree;  display:flex; overflow:hidden; white-space:nowrap; height: 23px;  font-size: 13px; padding-top: 1px; align-items: center;">
        <span style="margin-left: 10px;" id="subFilter" title="event filter">none</span>
    </div>
    <div style="grid-area: ev-title-events; display:flex; overflow:hidden; white-space:nowrap; height: 23px;  font-size: 13px; padding-top: 1px; align-items: center;" class="nobreak">


        <div  style="margin-right: 0x;" title="Clear log" class="navigate" onclick="app.events.clearAllEvents()" ><div style="opacity: 0.5; margin-top: 3px; font-size: 11px;">🗑️</div></div>
        <span style="margin-left: 3px;">log</span>     <span id="logCount"   style="margin-left: 5px; min-width: 35px; margin-right: 5px;">0</span>

        <span style="margin-left:  0px;">events</span> <span id="eventCount" style="margin-left: 5px; min-width: 35px; margin-right: 5px;">0</span>

        <label style="margin-left: 10px;" for="eventSeqStart"       title="filter seq start in log">seq</label>
        <input style="margin-left: 5px; width:40px;"  type="text"   title="filter seq start in log" id="eventSeqStart" name="eventSeqStart">

        <label style="margin-left: 5px;" for="eventSeqEnd"          title="filter seq end in log">to</label>
        <input style="margin-left: 5px; width:40px;"  type="text"   title="filter seq end in log" id="eventSeqEnd" name="eventSeqEnd">
        
        <label style="margin-left: 10px;" for="eventSrcFilter"      title="filter src users in log">src</label>
        <input style="margin-left: 5px; width:100px;" type="text"   title="filter src users in log" id="eventSrcFilter" name="eventSrcFilter" spellcheck="false">

        <input style="margin-left: 10px;" type="checkbox" checked id="scrollToEnd" title="Scroll to end on new events">
        <label for="scrollToEnd" title="Scroll to end on new events">Scroll to End</label>

        <input style="margin-left: 10px;" type="checkbox" checked id="prettifyEvents" title="Prettify new events">
        <label for="prettifyEvents"  title="Prettify new events">Prettify</label>

    </div>
    <div style="grid-area: ev-tree; background-color: var(--background2); overflow: auto; padding: 5px">
      <div class="catalogs" id="subscriptionTree" style="display: block; min-height: 50px; white-space: nowrap;">
        <li id="allEvents" title="show all events" class="clusterContainer" onclick="app.events.selectAllEvents(this)"><div style="opacity: 0.5;">&nbsp;all</div></li>
          <!-- [How To Create a Tree View] https://www.w3schools.com/howto/howto_js_treeview.asp -->
      </div>
    </div>
    <div id="evBar1" style="grid-area: ev-bar1; cursor: ew-resize;" class="vbar" onpointerdown="app.startDrag(event, 'events', id, true)"></div>
    <div class="editor" style="grid-area: ev-editor; overflow: visible;">
        <div id="eventsContainer" class="editor"></div>
    </div>
  </div>

  <!-- ------------------------------------------- Settings ------------------------------------------- -->
  <div id="settings" class="tabContent" style="grid-area: body-settings; overflow: hidden; padding: 10px;">
  <div>
    <div>Settings</div>
    <div class="settings">
      <ul>
        <li>
            <input type="checkbox" id="showLineNumbers"  onclick="app.changeConfig(id, checked)">
            <label for="showLineNumbers">Editor: Line numbers</label>
        </li>
        <li>
            <input type="checkbox" id="showMinimap"  onclick="app.changeConfig(id, checked)">
            <label for="showMinimap">Editor: Minimap</label>
        </li>
        <br>
        <li>
          <input type="checkbox" id="formatEntities"  onclick="app.changeConfig(id, checked)">
          <label for="formatEntities">Format entities received from server</label>
        </li>
        <li>
          <input type="checkbox" id="formatResponses" onclick="app.changeConfig(id, checked)">
          <label for="formatResponses">Format responses</label>
        </li>
      </ul>
    </div>
    <br>
    <div>support schemas</div>
    <div class="settings">
      <table style="font-size: 14px; margin-left: 20px;">
        <tr>
          <td>
              <a class="docLink" target='_blank' rel='noopener' href="./schema/protocol/html/schema.html" >Protocol</a>
              <a class="diagram" target='_blank' rel='noopener' href="./schema/protocol/html/class-diagram.html">CD</a>
          </td>
          <td style="opacity: 0.6; padding-left: 10px;">batch API used in Playground</td>
        </tr>
        <tr>
          <td>
            <a class="docLink" target='_blank' rel='noopener' href="./schema/filter/html/schema.html">FilterOperation</a>
            <a class="diagram" target='_blank' rel='noopener' href="./schema/filter/html/class-diagram.html">CD</a>
          </td>
          <td style="opacity: 0.6; padding-left: 10px;">filterTree in Protocol tasks: query & aggregate</td>
        </tr>
        <tr>
          <td>
            <a class="docLink" target='_blank' rel='noopener' href="./schema/json-schema/html/schema.html">JSON Schema</a>
            <a class="diagram" target='_blank' rel='noopener' href="./schema/json-schema/html/class-diagram.html">CD</a>
          </td>
          <td style="opacity: 0.6; padding-left: 10px;">jsonSchemas in ClusterStore jsonSchemas</td>
        </tr>
      </table>
    </div>  
    
    <div style="font-size: 12px; position: absolute; right: 0; bottom: 0; margin: 10px;">
        <div>
            <a href="https://github.com/friflo/Friflo.Json.Fliox" target='_blank' rel='noopener'>friflo JSON Fliox</a><br/>
            <span id="flioxVersion" style="opacity: 0.6;"></span><br/>
            <span style="opacity: 0.6; ">Copyright © 2023 Ullrich Praetz</span>
        </div>
    </div>
  </div>  
  </div>
  
</body>
</html>